Skip to content

feat: allow copy config from existing configs#6785

Open
Flartiny wants to merge 2 commits intoAstrBotDevs:masterfrom
Flartiny:feat/copy-config
Open

feat: allow copy config from existing configs#6785
Flartiny wants to merge 2 commits intoAstrBotDevs:masterfrom
Flartiny:feat/copy-config

Conversation

@Flartiny
Copy link
Contributor

@Flartiny Flartiny commented Mar 22, 2026

feat mentioned by #4020
允许从已存在的配置文件中复制,减少重复操作。
并且注意到此前是允许配置文件重名的,不利于辨识,故前端添加了校验(应该不影响已经创建的重名文件)。

Modifications / 改动点

dashboard/src/views/ConfigPage.vue
以及i18n(RU没能力做进一步校验了)

  • This is NOT a breaking change. / 这不是一个破坏性变更。

Screenshots or Test Results / 运行截图或测试结果

image image

Checklist / 检查清单

  • 😊 If there are new features added in the PR, I have discussed it with the authors through issues/emails, etc.
    / 如果 PR 中有新加入的功能,已经通过 Issue / 邮件等方式和作者讨论过。

  • 👀 My changes have been well-tested, and "Verification Steps" and "Screenshots" have been provided above.
    / 我的更改经过了良好的测试,并已在上方提供了“验证步骤”和“运行截图”

  • 🤓 I have ensured that no new dependencies are introduced, OR if new dependencies are introduced, they have been added to the appropriate locations in requirements.txt and pyproject.toml.
    / 我确保没有引入新依赖库,或者引入了新依赖库的同时将其添加到 requirements.txtpyproject.toml 文件相应位置。

  • 😮 My changes do not introduce malicious code.
    / 我的更改没有引入恶意代码。

Summary by Sourcery

Add ability to create new configs by copying existing ones and improve config name handling in the dashboard.

New Features:

  • Allow users to copy an existing configuration to create a new one with its settings pre-filled.

Enhancements:

  • Add validation to prevent duplicate or empty configuration names and update the config form title and save state accordingly.
  • Expose copy, edit, and delete actions consistently in the config list UI and extend i18n strings for the new copy and validation messages.

@auto-assign auto-assign bot requested review from Fridemn and Soulter March 22, 2026 06:39
@dosubot dosubot bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Mar 22, 2026
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

此拉取请求为配置管理页面带来了显著改进,引入了便捷的配置复制功能,使用户能够基于现有配置快速创建新配置,从而大幅减少了手动重复工作。同时,为了解决配置名称可能重复的问题,前端现在强制执行名称唯一性校验,并在用户尝试使用已存在的名称时提供即时反馈。这些更新共同提升了用户体验,使配置管理流程更加高效、直观且不易出错。

Highlights

  • 配置复制功能: 新增了配置复制功能,允许用户从现有配置创建新配置,减少重复操作。
  • 前端名称校验: 在前端实现了配置名称唯一性校验,防止创建或保存重名配置,提升配置管理的清晰度。
  • 多语言支持: 更新了多语言文件(en-US, ru-RU, zh-CN),为“复制配置”、“名称已存在”和“复制失败”等新功能和错误信息提供了翻译。
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@dosubot dosubot bot added the area:webui The bug / feature is about webui(dashboard) of astrbot. label Mar 22, 2026
Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • There is quite a bit of duplicated logic for initializing/resetting the config form state across startCreateConfig, startEditConfig, startCopyConfig, and cancelConfigForm; consider extracting a single helper (e.g. resetConfigForm({ mode, config })) to avoid future drift and make the form behavior easier to reason about.
  • Name normalization is currently performed in several places (saveConfigForm, createNewConfig, updateConfigInfo, copyConfig); since saveConfigForm already normalizes and writes back to configFormData.name, you could pass the normalized name down as an argument to the create/update/copy helpers and avoid repeated normalization and potential inconsistencies.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- There is quite a bit of duplicated logic for initializing/resetting the config form state across `startCreateConfig`, `startEditConfig`, `startCopyConfig`, and `cancelConfigForm`; consider extracting a single helper (e.g. `resetConfigForm({ mode, config })`) to avoid future drift and make the form behavior easier to reason about.
- Name normalization is currently performed in several places (`saveConfigForm`, `createNewConfig`, `updateConfigInfo`, `copyConfig`); since `saveConfigForm` already normalizes and writes back to `configFormData.name`, you could pass the normalized name down as an argument to the create/update/copy helpers and avoid repeated normalization and potential inconsistencies.

## Individual Comments

### Comment 1
<location path="dashboard/src/views/ConfigPage.vue" line_range="160" />
<code_context>
             <v-btn variant="text" @click="cancelConfigForm">{{ tm('buttons.cancel') }}</v-btn>
             <v-btn color="primary" @click="saveConfigForm"
-              :disabled="!configFormData.name">
+              :disabled="!configFormData.name || (isCopyingConfig && !copySourceConfigId)">
               {{ isEditingConfig ? tm('buttons.update') : tm('buttons.create') }}
             </v-btn>
</code_context>
<issue_to_address>
**issue (bug_risk):** The save button can be enabled for whitespace-only names, leading to a validation error on submit.

`saveConfigForm` trims the name via `normalizeConfigName` and rejects empty results, but the disabled state uses the raw `configFormData.name`. With a whitespace-only name the button enables, then submit fails. Base `:disabled` on the trimmed value instead (e.g. `!normalizeConfigName(configFormData.name)`, maybe via a computed property) so the button state matches the validation logic.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a useful feature for copying existing configurations, which will save users time. It also adds important frontend validation to prevent duplicate configuration names. The implementation is solid. I've provided a couple of suggestions to refactor parts of the Vue component for better readability and maintainability, such as using a computed property for a complex template expression and converting a promise chain to async/await.

Comment on lines +148 to +150
<h3 class="mb-4">
{{ isEditingConfig ? tm('configManagement.editConfig') : (isCopyingConfig ? tm('configManagement.copyConfig') : tm('configManagement.newConfig')) }}
</h3>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic for determining the form title is getting a bit complex to be inlined in the template. To improve readability and maintainability, consider extracting this logic into a computed property.

For example, you could add a configFormTitle computed property:

computed: {
  // ... other computed properties
  configFormTitle() {
    if (this.isEditingConfig) {
      return this.tm('configManagement.editConfig');
    }
    if (this.isCopyingConfig) {
      return this.tm('configManagement.copyConfig');
    }
    return this.tm('configManagement.newConfig');
  }
}

Then, you can simplify the template:

          <h3 class="mb-4">{{ configFormTitle }}</h3>

Comment on lines +733 to +768
copyConfig() {
const configName = this.normalizeConfigName(this.configFormData.name);
axios.get('/api/config/abconf', {
params: { id: this.copySourceConfigId }
}).then((res) => {
const sourceConfig = res.data?.data?.config;
if (!sourceConfig) {
this.save_message = this.tm('configManagement.copyFailed');
this.save_message_snack = true;
this.save_message_success = "error";
return;
}
return axios.post('/api/config/abconf/new', {
name: configName,
config: sourceConfig
});
}).then((res) => {
if (!res) return;
if (res.data.status === "ok") {
this.save_message = res.data.message;
this.save_message_snack = true;
this.save_message_success = "success";
this.getConfigInfoList(res.data.data.conf_id);
this.cancelConfigForm();
} else {
this.save_message = res.data.message;
this.save_message_snack = true;
this.save_message_success = "error";
}
}).catch((err) => {
console.error(err);
this.save_message = err?.response?.data?.message || this.tm('configManagement.copyFailed');
this.save_message_snack = true;
this.save_message_success = "error";
});
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The copyConfig method uses promise chaining with .then(). While this works, refactoring it to use async/await would make the code more linear, readable, and easier to debug. The error handling can also be simplified with a single try...catch block.

    async copyConfig() {
      const configName = this.normalizeConfigName(this.configFormData.name);
      try {
        const getRes = await axios.get('/api/config/abconf', {
          params: { id: this.copySourceConfigId }
        });

        const sourceConfig = getRes.data?.data?.config;
        if (!sourceConfig) {
          throw new Error('Failed to fetch source configuration for copying.');
        }

        const postRes = await axios.post('/api/config/abconf/new', {
          name: configName,
          config: sourceConfig
        });

        if (postRes.data.status === "ok") {
          this.save_message = postRes.data.message;
          this.save_message_snack = true;
          this.save_message_success = "success";
          this.getConfigInfoList(postRes.data.data.conf_id);
          this.cancelConfigForm();
        } else {
          throw new Error(postRes.data.message || this.tm('configManagement.copyFailed'));
        }
      } catch (err) {
        console.error(err);
        this.save_message = err?.response?.data?.message || err.message || this.tm('configManagement.copyFailed');
        this.save_message_snack = true;
        this.save_message_success = "error";
      }
    },

- duplicated logic for initializing/resetting the config
- check whitespace-only names
@Flartiny
Copy link
Contributor Author

@sourcery-ai review

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:webui The bug / feature is about webui(dashboard) of astrbot. size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant